interface iPixelShader {
	isLife(): boolean;
	isEnd(): boolean;
	doUpdate(time: number): void;
	release(): void;
}

class PixelShaderMan {
	private static instance: PixelShaderMan;

	public static ins(): PixelShaderMan {
		if (!this.instance) {
			this.instance = new PixelShaderMan();
		}
		return this.instance;
	}

	shaders: iPixelShader[] = [];

	doUpdate(delta: number) {
		const ends = [];
		for (const shader of this.shaders) {
			if (shader.isLife()) {
				shader.doUpdate(delta);
			}
			if (shader.isEnd()) {
				ends.push(shader);
			}
		}
		for (const e of ends) {
			this.removeShader(e);
		}
	}

	addShader(ps: iPixelShader) {
		this.shaders.push(ps);
	}

	removeShader(ps: iPixelShader) {
		this.shaders.remove(ps);
	}
}

class PixelShader implements iPixelShader {
	obj: egret.DisplayObject;

	life = 1000; // 毫秒

	constructor(obj: egret.DisplayObject, life = 2000) {
		this.life = life;
		this.obj = obj;
	}

	timeMul = 1;

	setTimeMul(mul: number) {
		if (mul < 0) {
			this.shaderToy.iTime = -mul;
		}
		this.timeMul = mul;
	}

	shaderToy = {
		progress: 0,
		iTime: 0,
		iResolution: { x: 0, y: 0 },
		iPos: { x: 0, y: 0 },
	};

	init() {
		this.shaderToy.iTime = 0;
	}

	setPos(x: number, y: number) {
		this.shaderToy.iPos.x = x;
		this.shaderToy.iPos.y = y;
	}

	release(): void {
		this.removeTimer();
		if (this.obj) {
			this.obj.filters = [];
			this.obj = null;
		}
	}

	isLife() {
		return true;
	}

	isEnd() {
		return false;
	}

	doUpdate(time: number) {
		this.shaderToy.iResolution.x = this.obj.width;
		this.shaderToy.iResolution.y = this.obj.height;
		this.shaderToy.iTime += (time / 1000) * this.timeMul;
		this.life -= time;
		if (this.shaderToy.iTime < 0) {
			this.shaderToy.iTime = 0;
		}
		// if (this.shaderToy.iTime > 1) {
		// 	this.shaderToy.iTime = 1;
		// }
		this.shaderToy.progress = Math.min(1.0, (this.shaderToy.iTime * 1000) / this.life);
		return false;
	}

	addTimer() {
		PixelShaderMan.ins().addShader(this);
	}

	removeTimer() {
		PixelShaderMan.ins().removeShader(this);
	}

	async setShader(file: string) {
		if (!file) {
			this.obj.filters = [];
			return;
		}
		const filtter = await asyncGetFillter(file, this.shaderToy);
		if (filtter) {
			// ui.msglog('load shader', file);
			this.obj.filters = [filtter];
		}
	}

	autoAdd() {
		const { obj } = this;
		obj.addEventListener(egret.Event.ADDED_TO_STAGE, this.onAdd, this);
		obj.addEventListener(egret.Event.REMOVED_FROM_STAGE, this.onRemove, this);
		if (obj.stage) {
			this.onAdd();
		}
	}

	onAdd(): void {
		this.addTimer();
	}

	onRemove(): void {
		this.removeTimer();
	}
}

// 绑定场景对象的shader，一直执行
// 优点在于不自动删除timer
class PixelShaderAuto extends PixelShader {
	constructor(obj: egret.DisplayObject, life = 2000) {
		super(obj, life);
		this.autoAdd();
	}
}

class PixelShaderWave extends PixelShaderAuto {
	angle = 5;

	override timeMul = 5;

	override async setShader(file: string) {
		this.angle = Number(file);
	}

	override doUpdate(time: number) {
		const rt = super.doUpdate(time);
		this.obj.rotation += (time / 1000) * this.timeMul;
		if (this.obj.rotation >= this.angle) {
			this.obj.rotation = this.angle;
			this.timeMul *= -1;
		} else if (this.obj.rotation < -this.angle) {
			this.obj.rotation = -this.angle;
			this.timeMul *= -1;
		}
		return rt;
	}

	override release(): void {
		this.removeTimer();
	}
}

// 绑定ui对象的shader
class PixelShaderLife extends PixelShader {
	constructor(obj: egret.DisplayObject, life: number, callback: dramaCallback) {
		super(obj, life);

		this.callback = callback;
		this.addTimer();
		// const timer = new egret.Timer(life, 1);
		// timer.start();
		// timer.addEventListener('timer', this.onTimer, this);
	}

	callback: dramaCallback;

	// onTimer() {
	// 	// this.release();
	// 	this.removeTimer();
	// }
	override isLife() {
		return this.life > 0;
	}

	override isEnd() {
		if (this.isLife()) {
			return false;
		}
		if (this.callback) {
			const func = this.callback;
			this.callback = null;
			func(1);
		}
		return true;
	}
}

function createShaderByName(shader: string, obj: egret.DisplayObject): PixelShader {
	// 常见shader，通过一个string来实现，
	// 如果需要参数, 可以通过wave:alpha:mul来设定
	if (shader === 'wave') {
		const sd = new PixelShaderWave(obj);
		return sd;
	}
	return null;
}
